#pragma once
#include "Socket.hpp"
#include <iostream>
#include <sys/select.h>
#include <sys/time.h>

static const uint16_t defaultPort = 8888;
static const int fdNumMax = (sizeof(fd_set) * 8);
static const int defaultFd = -1;

class SelectServer
{
public:
    SelectServer(const uint16_t port = defaultPort) :_port(port)
    {
        for (int i = 0; i < fdNumMax; ++i)
        {
            _fd_array[i] = defaultFd;
        }
    }

    ~SelectServer()
    {}

    bool Init()
    {
        _listensock.Socket();
        _listensock.Bind(_port);
        _listensock.Listen();
        return true;
    }

    void Start()
    {
        int listenfd = _listensock.GetFd();
        _fd_array[0] = listenfd;
        for(;;)
        {
            // 每次进来都要重新设置rfds
            fd_set rfds;
            FD_ZERO(&rfds);

            int maxfd = _fd_array[0];
            for (int i = 0; i < fdNumMax; ++i)
            {
                if (_fd_array[i] == defaultFd)  continue;
                FD_SET(_fd_array[i], &rfds);
                if (maxfd < _fd_array[i])
                {
                    maxfd = _fd_array[i];
                    LOG(Info, "maxfd update, maxfd is %d", maxfd);
                }
            }

            //不能直接accept!检测并获取listensock上面的事件,新连接到来,等价于读事件就绪

            struct timeval timeout = {1,0};
            // 如果事件就绪，上层不处理，select会一直通知你！
            // select告诉你就绪了,接下来的一次读取,我们读取fd的时候,不会被阻塞
            // 如果timeout = {0,0},则是非阻塞轮询,和fcntl一样; 如果&timeout = nullptr的话则是阻塞
            // int n = select(maxfd + 1, &rfds, nullptr, nullptr, &timeout);
            int n = select(maxfd + 1, &rfds, nullptr, nullptr, nullptr);
            switch (n)
            {
            case 0:
                std::cout << "timeout" << std::endl;
                break;
            case -1:
                std::cerr << "select error" << std::endl;
                break;
            default:
                // listen有事件就绪了,TODO......
                Dispatcher(rfds);
                break;
            }
        }
    }

    void Dispatcher(fd_set &rfds)
    {
        for (int i = 0; i < fdNumMax; ++i)
        {
            int fd = _fd_array[i];
            if (fd == defaultFd) continue;
            
            if (FD_ISSET(fd, &rfds))
            {
                if (fd == _listensock.GetFd())  // 说明是listenfd就绪,代表可以Accept了
                {
                    std::cout << "Accept就绪< get a new link!" << std::endl;
                    Accepter();  // 连接管理器
                }
                else  // 说明可以进行接收报文了
                {
                    std::cout << "Recvfd就绪" << std::endl;
                    Recver(fd, i);
                }
            }
        }
    }

    void Accepter()
    {
        // 我们的连接事件就绪了,可以进行Accept了;
        std::string clientip;
        uint16_t clientport = 0;
        int sock = _listensock.Accept(&clientip, &clientport);
        if (sock < 0)
        {
            return;
        }
        LOG(Info, "accept success, %s: %d, sock fd: %d", clientip.c_str(), clientport, sock);

        // 找到_fd_array中的空位,将accept到的clientfd存放到_fd_array中,以便将accept到的客户端加入到select中
        int pos = 1;
        for (; pos < fdNumMax; ++pos)
        {
            if (_fd_array[pos] != defaultFd) continue;
            else break;
        }
        if (pos == fdNumMax)
        {
            LOG(Warning, "server is full, close %d now!", sock);
            close(sock);
        }
        else
        {
            // 将accept到的clientfd存放到_fd_array中,以便将accept到的客户端加入到select中
            _fd_array[pos] = sock;
            PrintFd();
        }
    }

    void Recver(int fd, int pos)
    {
        char buffer[1024];
        ssize_t n = read(fd, buffer, sizeof(buffer) - 1);  // 实际中Tcp按字节流发包,应用层需要需要定制协议来判断是否接收完整
        if (n > 0)
        {
            buffer[n] = 0;
            std::cout << "get a message: " << buffer << std::endl;
        }
        else if (n == 0)
        {
            LOG(Info, "client quit, me too, close fd: %d", fd);
            close(fd);
            _fd_array[pos] == defaultFd;  // 本质就算将该客户端的fd从select中移除
        }
        else
        {
            LOG(Warning, "recv error, fd: %d", fd);
            close(fd);
            _fd_array[pos] = defaultFd;  // 本质就算将该客户端的fd从select中移除
        }
    }

    void PrintFd()
    {
        std::cout << "online fd list: ";
        for (int i = 0; i < fdNumMax; i++)
        {
            if (_fd_array[i] == defaultFd)
                continue;
            std::cout << _fd_array[i] << " ";
        }
        std::cout << std::endl;
    }

private:
    Sock _listensock;
    uint16_t _port;
    int _fd_array[fdNumMax];
};  